package com.rabbitmq.origin.api;

import com.rabbitmq.client.*;
import com.rabbitmq.common.utils.RabbitMqUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;

import static com.rabbitmq.common.constants.CommonConstants.DLX_QUEUE_NAME_PREFIX;


/**
 * @author
 * @Describe
 *
 * 1.死信消息：
 *      消息被拒绝（Basic.Reject或Basic.Nack）并且设置消息不会重新如队列即requeue参数的值为false
 *      消息过期了
 *      队列达到最大的长度
 * 2.过期消息：在 rabbitmq 中存在2种方可设置消息的过期时间:
 *      第一种通过对队列进行设置，这种设置后，该队列中所有的消息都存在相同的过期时间。
 *      第二种通过对消息本身进行设置，那么每条消息的过期时间都不一样。
 *      如果同时使用这2种方法，那么以过期时间小的那个数值为准。当消息达到过期时间还没有被消费，那么那个消息就成为了一个死信消息。
 *          队列设置：在队列申明的时候使用 x-message-ttl 参数，单位为 毫秒
 *          单个消息设置：是设置消息属性的 expiration 参数的值，单位为 毫秒
 *
 * 3.死信交换器：DLX 全称（Dead-Letter-Exchange）,称之为死信交换器。
 *      当消息变成一个死信之后，如果这个消息所在的队列存在x-dead-letter-exchange参数，那么它会被发送到x-dead-letter-exchange对应值的交换器上，这个交换器就称之为死信交换器
 * 4.死信队列: 与死信交换器绑定的队列就是死信队列。
 *
 * 5.主题类型交换机,该模式为消息主题模式。在直连类型的基础上，主题类型支持通配符路由匹配,通配符如下：
 * 	    * 匹配不多不少恰好1个词
 * 	    # 匹配零个、一个或多个词
 *      如: audit.#,匹配audit、audit.irs 、或者audit.irs.corporate等,audit.*只能匹配 audit.irs
 * @date
 */
@Slf4j(topic = "RabbitMqProviderTopicDeadLetterx")
public class RabbitMqProviderTopicDeadLetterx {

    /**
     * 主题方式，队列存活时间设置
     */
    public void publishTopicQueueTtlDeadLetterx(String exchangeName, String routingKey, String queueName, String msg) {
        Connection conn = null;
        Channel channel = null;
        try {
            // 获取连接
            conn = RabbitMqUtil.getConn();
            // 创建通道（通道可用于消息的发送和接收）
            channel = conn.createChannel();

            // 死信交换机名
            String DLXExchangeName = DLX_QUEUE_NAME_PREFIX + exchangeName;
            // 死信队列名
            String DLXQueueName = DLX_QUEUE_NAME_PREFIX + queueName;
            // 死信绑定路由key
            String DLXRoutingKey = DLX_QUEUE_NAME_PREFIX + routingKey;

            /************1.声明交换机和队列并绑定***********/
            // 主题方式的交换机
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC, true);

            // 队列参数
            Map<String, Object> arguments = new HashMap<String,Object>();
            // 为队列设置DLX参数
            arguments.put("x-dead-letter-exchange", DLXExchangeName);
            // 为队列设置DLK路由参数x-dead-letter-routing-key
            arguments.put("x-dead-letter-routing-key", DLXRoutingKey);
            // 设置队列中的消息过期时间为10s,超时队列中的消息将会发送到死信交换机上，从而经过路由到死信队列里
            arguments.put("x-message-ttl", 10000);
            // 声明队列并设置参数
            channel.queueDeclare(queueName, true, false, false, arguments);
            // 队列绑定交换机
            channel.queueBind(queueName, exchangeName, routingKey);

            /************2.声明死信交换机和死信队列并绑定***********/
            // 声明死信队列并设置属性
            channel.exchangeDeclare(DLXExchangeName, BuiltinExchangeType.TOPIC, false, true, null);
            // 死信队列参数
            Map<String, Object> dlxArguments = new HashMap<String,Object>();
            // 为队列设置x-queue-mode参数,将队列设置为延迟模式,在磁盘上保留尽可能多的消息,以减少RAM的使用;
            // 如果未设置，队列将保留内存缓存以尽可能快地传递消息;默认值: false.
            dlxArguments.put("x-queue-mode", "lazy");
            // 设置队列中的消息过期时间为10s,超时队列中的消息将会被删除
            // dlxArguments.put("x-message-ttl", 10000);
            // 声明死信队列
            channel.queueDeclare(DLXQueueName, true, false, false, dlxArguments);
            // 绑定
            channel.queueBind(DLXQueueName, DLXExchangeName, DLXRoutingKey);

            // 发布消息
            channel.basicPublish(exchangeName, routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, msg.getBytes());
        } catch (Exception e) {
            log.error("发布消息异常,", e);
        } finally {
            // log.info("[RabbitMqProviderTopicDeadLetterx] publishTopicDeadLetterx 关闭资源连接");
            RabbitMqUtil.close(conn, channel);
        }
    }

    /**
     * 主题方式，单个消息设置过期时间
     */
    public void publishTopicSingleMsgTtlDeadLetterx(String exchangeName, String routingKey, String queueName, String msg, String expiration) {
        Connection conn = null;
        Channel channel = null;
        try {
            // 获取连接
            conn = RabbitMqUtil.getConn();
            // 创建通道（通道可用于消息的发送和接收）
            channel = conn.createChannel();

            // 死信交换机名
            String DLXExchangeName = DLX_QUEUE_NAME_PREFIX + exchangeName;
            // 死信队列名
            String DLXQueueName = DLX_QUEUE_NAME_PREFIX + queueName;
            // 死信绑定路由key
            String DLXRoutingKey = DLX_QUEUE_NAME_PREFIX + routingKey;

            /************1.声明交换机和队列并绑定***********/
            // 主题方式的交换机
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC, true);
            // 死信队列参数
            Map<String, Object> arguments = new HashMap<String,Object>();
            // 为队列设置DLX参数
            arguments.put("x-dead-letter-exchange", DLXExchangeName);
            // 为队列设置DLK路由参数x-dead-letter-routing-key
            arguments.put("x-dead-letter-routing-key", DLXRoutingKey);
            // 声明队列并设置参数
            channel.queueDeclare(queueName, true, false, false, arguments);
            // 队列绑定交换机
            channel.queueBind(queueName, exchangeName, routingKey);

            /************2.声明死信交换机和死信队列并绑定***********/
            // 声明死信队列并设置属性
            channel.exchangeDeclare(DLXExchangeName, BuiltinExchangeType.TOPIC, false, true, null);
            // 死信队列参数
            Map<String, Object> dlxArguments = new HashMap<String,Object>();
            // 为队列设置x-queue-mode参数,将队列设置为延迟模式,在磁盘上保留尽可能多的消息,以减少RAM的使用;
            // 如果未设置，队列将保留内存缓存以尽可能快地传递消息;默认值: false.
            dlxArguments.put("x-queue-mode", "lazy");
            // 设置队列中的消息过期时间为10s,超时队列中的消息将会被删除
            // dlxArguments.put("x-message-ttl", 10000);
            // 声明死信队列
            channel.queueDeclare(DLXQueueName, true, false, false, dlxArguments);
            // 绑定
            channel.queueBind(DLXQueueName, DLXExchangeName, DLXRoutingKey);

            // 消息属性
            AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
            // 设置超时时间
            builder.expiration(expiration);
            AMQP.BasicProperties properties = builder.build();
            // 发布消息
            channel.basicPublish(exchangeName, routingKey, properties, msg.getBytes());
        } catch (Exception e) {
            log.error("发布消息异常,", e);
        } finally {
            // log.info("[RabbitMqProviderTopicDeadLetterx] publishTopicDeadLetterx 关闭资源连接");
            RabbitMqUtil.close(conn, channel);
        }
    }
}
